﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using l1cs.server.model.Instance;
using System.Text.RegularExpressions;
using l1cs.server.serverpackets;
using l1cs.server.datatables;
using l1cs.server.templates;
using l1cs.server.utils;
using l1cs.server.model;

namespace l1cs.server.clientpackets
{

    public class C_CreateChar : ClientBasePacket
    {
        private static Logger _log = Logger.getLogger( typeof( C_CreateChar ).FullName );
        private const String _C_CREATE_CHAR = "[C] C_CreateChar";

        private static int[] ORIGINAL_STR = new int[] { 13, 16, 11, 8, 12, 13, 11 };
        private static int[] ORIGINAL_DEX = new int[] { 10, 12, 12, 7, 15, 11, 10 };
        private static int[] ORIGINAL_CON = new int[] { 10, 14, 12, 12, 8, 14, 12 };
        private static int[] ORIGINAL_WIS = new int[] { 11, 9, 12, 12, 10, 12, 12 };
        private static int[] ORIGINAL_CHA = new int[] { 13, 12, 9, 8, 9, 8, 8 };
        private static int[] ORIGINAL_INT = new int[] { 10, 8, 12, 12, 11, 11, 12 };
        private static int[] ORIGINAL_AMOUNT = new int[] { 8, 4, 7, 16, 10, 6, 10 };

        private static String CLIENT_LANGUAGE_CODE = Config.CLIENT_LANGUAGE_CODE;

        public C_CreateChar( byte[] abyte0, ClientThread client )
            : base( abyte0 )
        {
            L1PcInstance pc = new L1PcInstance();
            String name = readS();

            Account account = new Account();
            account.load( client.getAccountName() );
            int characterSlot = account.getCharacterSlot();
            int maxAmount = Config.DEFAULT_CHARACTER_SLOT + characterSlot;

            name = Regex.Replace( name, "\\s", "" ); //("\\s//name.replaceAll("\\s", "");
            name = Regex.Replace( name, "　", "" );//name.replaceAll("　", "");
            if ( name.Length == 0 )
            {
                S_CharCreateStatus s_charcreatestatus = new S_CharCreateStatus( S_CharCreateStatus.REASON_INVALID_NAME );
                client.sendPacket( s_charcreatestatus );
                return;
            }

            if ( isInvalidName( name ) )
            {
                S_CharCreateStatus s_charcreatestatus = new S_CharCreateStatus( S_CharCreateStatus.REASON_INVALID_NAME );
                client.sendPacket( s_charcreatestatus );
                return;
            }

            if ( CharacterTable.doesCharNameExist( name ) )
            {
                _log.fine( "charname: " + pc.getName() + " already exists. creation failed." );
                S_CharCreateStatus s_charcreatestatus1 = new S_CharCreateStatus( S_CharCreateStatus.REASON_ALREADY_EXSISTS );
                client.sendPacket( s_charcreatestatus1 );
                return;
            }

            if ( client.getAccount().countCharacters() >= maxAmount )
            {
                _log.fine( "account: " + client.getAccountName() + " " + maxAmount + "を超えるキャラクター作成要求。" );
                S_CharCreateStatus s_charcreatestatus1 = new S_CharCreateStatus( S_CharCreateStatus.REASON_WRONG_AMOUNT );
                client.sendPacket( s_charcreatestatus1 );
                return;
            }

            pc.setName( name );
            pc.setType( readC() );
            pc.set_sex( readC() );
            pc.addBaseStr( (byte)readC() );
            pc.addBaseDex( (byte)readC() );
            pc.addBaseCon( (byte)readC() );
            pc.addBaseWis( (byte)readC() );
            pc.addBaseCha( (byte)readC() );
            pc.addBaseInt( (byte)readC() );

            bool isStatusError = false;
            int originalStr = ORIGINAL_STR[pc.getType()];
            int originalDex = ORIGINAL_DEX[pc.getType()];
            int originalCon = ORIGINAL_CON[pc.getType()];
            int originalWis = ORIGINAL_WIS[pc.getType()];
            int originalCha = ORIGINAL_CHA[pc.getType()];
            int originalInt = ORIGINAL_INT[pc.getType()];
            int originalAmount = ORIGINAL_AMOUNT[pc.getType()];

            if ( ( pc.getBaseStr() < originalStr
                || pc.getBaseDex() < originalDex
                || pc.getBaseCon() < originalCon
                || pc.getBaseWis() < originalWis
                || pc.getBaseCha() < originalCha
                || pc.getBaseInt() < originalInt )
                || ( pc.getBaseStr() > originalStr + originalAmount
                || pc.getBaseDex() > originalDex + originalAmount
                || pc.getBaseCon() > originalCon + originalAmount
                || pc.getBaseWis() > originalWis + originalAmount
                || pc.getBaseCha() > originalCha + originalAmount
                || pc.getBaseInt() > originalInt + originalAmount ) )
            {
                isStatusError = true;
            }

            int statusAmount = pc.getDex() + pc.getCha() + pc.getCon()
                    + pc.getInt() + pc.getStr() + pc.getWis();

            if ( statusAmount != 75 || isStatusError )
            {
                _log.finest( "Character have wrong value" );
                S_CharCreateStatus s_charcreatestatus3 = new S_CharCreateStatus( S_CharCreateStatus.REASON_WRONG_AMOUNT );
                client.sendPacket( s_charcreatestatus3 );
                return;
            }

            _log.fine( "charname: " + pc.getName() + " classId: " + pc.getClassId() );
            S_CharCreateStatus s_charcreatestatus2 = new S_CharCreateStatus( S_CharCreateStatus.REASON_OK );
            client.sendPacket( s_charcreatestatus2 );
            initNewChar( client, pc );
        }

        private static int[] MALE_LIST = new int[] { 0, 61, 138, 734, 2786, 6658, 6671 };
        private static int[] FEMALE_LIST = new int[] { 1, 48, 37, 1186, 2796, 6661, 6650 };
        private static int[] LOCX_LIST = new int[] { 32734, 32734, 32734, 32734, 32734, 32734, 32734 };
        private static int[] LOCY_LIST = new int[] { 32798, 32798, 32798, 32798, 32798, 32798, 32798 };
        private static short[] MAPID_LIST = new short[] { 8013, 8013, 8013, 8013, 8013, 8013, 8013 };
        // private static final int[] LOCX_LIST = new int[] { 32780, 32714, 32714,
        // 32780, 32714, 32714, 32714 };
        // private static final int[] LOCY_LIST = new int[] { 32781, 32877, 32877,
        // 32781, 32877, 32877, 32877 };
        // private static final short[] MAPID_LIST = new short[] { 68, 69, 69, 68, 69,
        // 69, 69 };

        private static void initNewChar( ClientThread client, L1PcInstance pc )
        {
            pc.setId( IdFactory.getInstance().nextId() );
            if ( pc.get_sex() == 0 )
            {
                pc.setClassId( MALE_LIST[pc.getType()] );
            }
            else
            {
                pc.setClassId( FEMALE_LIST[pc.getType()] );
            }
            pc.setX( LOCX_LIST[pc.getType()] );
            pc.setY( LOCY_LIST[pc.getType()] );
            pc.setMap( MAPID_LIST[pc.getType()] );
            pc.setHeading( 0 );
            pc.setLawful( 0 );

            int initHp = CalcInitHpMp.calcInitHp( pc );
            int initMp = CalcInitHpMp.calcInitMp( pc );
            pc.addBaseMaxHp( (short)initHp );
            pc.setCurrentHp( (short)initHp );
            pc.addBaseMaxMp( (short)initMp );
            pc.setCurrentMp( (short)initMp );
            pc.resetBaseAc();
            pc.setTitle( "" );
            pc.setClanid( 0 );
            pc.setClanRank( 0 );
            pc.set_food( 40 );
            pc.setAccessLevel( (short)0 );
            pc.setGm( false );
            pc.setMonitor( false );
            pc.setGmInvis( false );
            pc.setExp( 0 );
            pc.setHighLevel( 0 );
            pc.setStatus( 0 );
            pc.setAccessLevel( (short)0 );
            pc.setClanname( "" );
            pc.setBonusStats( 0 );
            pc.setElixirStats( 0 );
            pc.resetBaseMr();
            pc.setElfAttr( 0 );
            pc.set_PKcount( 0 );
            pc.setPkCountForElf( 0 );
            pc.setExpRes( 0 );
            pc.setPartnerId( 0 );
            pc.setOnlineStatus( 0 );
            pc.setHomeTownId( 0 );
            pc.setContribution( 0 );
            pc.setBanned( false );
            pc.setKarma( 0 );
            if ( pc.isWizard() )
            { // WIZ
                pc.sendPackets( new S_AddSkill( 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ) );
                int object_id = pc.getId();
                L1Skills l1skills = SkillsTable.getInstance().getTemplate( 4 ); // EB
                String skill_name = l1skills.getName();
                int skill_id = l1skills.getSkillId();
                SkillsTable.getInstance().spellMastery( object_id, skill_id, skill_name, 0, 0 ); // DBに登録
            }
            Beginner.getInstance().GiveItem( pc );
            pc.setAccountName( client.getAccountName() );
            CharacterTable.getInstance().storeNewCharacter( pc );
            S_NewCharPacket s_newcharpacket = new S_NewCharPacket( pc );
            client.sendPacket( s_newcharpacket );
            //CharacterTable.getInstance().saveCharStatus( pc );
            CharacterTable.saveCharStatus( pc );
            pc.refresh();
        }

        private static bool isAlphaNumeric( String s )
        {
            bool flag = true;
            char[] ac = s.ToCharArray(); //toCharArray();
            int i = 0;
            do
            {
                if ( i >= ac.Length )
                {
                    break;
                }
                if ( !Char.IsLetterOrDigit( ac[i] ) ) // 文字かどうかチェック？
                {
                    flag = false;
                    break;
                }
                i++;
            } while ( true );
            return flag;
        }

        private static bool isInvalidName( String name )
        {
            int numOfNameBytes = 0;
            //numOfNameBytes = name.getBytes( CLIENT_LANGUAGE_CODE ).Length;
            numOfNameBytes = Encoding.GetEncoding( CLIENT_LANGUAGE_CODE ).GetByteCount( name );

            if ( isAlphaNumeric( name ) )
            {
                return false;
            }

            // XXX - 本鯖の仕様と同等か未確認
            // 全角文字が5文字を超えるか、全体で12バイトを超えたら無効な名前とする
            if ( 5 < ( numOfNameBytes - name.Length ) || 12 < numOfNameBytes )
            {
                return false;
            }

            if ( BadNamesList.getInstance().isBadName( name ) )
            {
                return false;
            }
            return true;
        }

        override
        public String getType()
        {
            return _C_CREATE_CHAR;
        }
    }
}
